<script setup>
import { reactive, ref, defineExpose, defineEmits, onBeforeUnmount, nextTick } from 'vue'
let page = reactive({
    x: 0,
    y: 0
})
let emits = defineEmits(['mouseenter'])
let isShow = ref(false)
let bodyClass = ref(['body'])
let pointClass = ref(['point'])
let type = 'bottom';
let offset = ref([0, 0]);
let autoTimmer;

// 获取元素的绝对位置坐标（像对于浏览器视区左上角）
function getElementViewPosition(element) {
    //计算x坐标
    var actualLeft = element.offsetLeft;
    var current = element.offsetParent;
    while (current !== null) {
        actualLeft += (current.offsetLeft + current.clientLeft);
        current = current.offsetParent;
    }
    if (document.compatMode == "BackCompat") {
        var elementScrollLeft = document.body.scrollLeft;
    } else {
        var elementScrollLeft = document.documentElement.scrollLeft;
    }
    var left = actualLeft - elementScrollLeft;
    //计算y坐标
    var actualTop = element.offsetTop;
    var current = element.offsetParent;
    while (current !== null) {
        actualTop += (current.offsetTop + current.clientTop);
        current = current.offsetParent;
    }
    if (document.compatMode == "BackCompat") {
        var elementScrollTop = document.body.scrollTop;
    } else {
        var elementScrollTop = document.documentElement.scrollTop;
    }
    var right = actualTop - elementScrollTop;
    //返回结果
    return { x: left, y: right }
}
function show($event, ...arr) {

    //自动隐藏
    if (isShow.value || !$event) {
        isShow.value = false;
        return
    }
    offset.value = [0, 0];
    //参数自动判断
    arr.forEach((item) => {
        if (typeof (item) == 'string') {
            type = item;
        } else if (typeof (item) == 'number') {
            if (offset.value[0] == 0) {
                offset.value[0] = item
            } else {
                offset.value[1] = item
            }
        }
    })

    //拿到元素位置信息
    let domOffset = $event.target.getBoundingClientRect()
    //计算位置
    let centerPoint = {
        top: {
            x: domOffset.x + domOffset.width / 2,
            y: domOffset.y,
        },
        left: {
            x: domOffset.x,
            y: domOffset.y + domOffset.height / 2,
        },
        right: {
            x: domOffset.x + domOffset.width,
            y: domOffset.y + domOffset.height / 2,
        },
        bottom: {
            x: domOffset.x + domOffset.width / 2,
            y: domOffset.y + domOffset.height,
        }
    }
    page = centerPoint[type]
    //动态添加class样式
    bodyClass.value[1] = type
    pointClass.value[1] = 'point' + type;
    //设置显示
    isShow.value = true

    //位置修正
    nextTick(() => {
        let body = document.body
        let dom = document.getElementById("dropDownList");
        let domPositon = getElementViewPosition(dom)
        isShow.value = false
        nextTick(() => {
            //计算修正参数
            let data = {
                top: dom.offsetTop,
                bottom: body.offsetHeight - (domPositon.y + dom.offsetHeight),
                left: dom.offsetLeft,
                right: body.offsetWidth - (domPositon.x + dom.offsetWidth)
            }
            if (data.top < 0) {
                offset.value[1] = -data.top / 2
            }
            if (data.bottom < 0) {
                offset.value[1] = data.top / 2
            }
            if (data.left < 0) {
                offset.value[0] = -data.left / 2
            }
            if (data.right < 0) {
                offset.value[0] = data.right / 2
            }
            //修正参数
            page.x += offset.value[0];
            page.y += offset.value[1];
            isShow.value = true
        })
    })
}

//控制取消显示
let timer;
function mouseEnter() {
    if (autoTimmer) {
        clearTimeout(autoTimmer)
    }
    if (timer) {
        clearTimeout(timer)
    }
    emits('mouseenter')
}
function mouseLeave() {
    timer = setTimeout(() => { isShow.value = false }, 500)
}

/* 
    监听点击事件
        当鼠标点击不在本组件区域内则关闭弹窗
 */
const mouseClick = (e) => {
    if (isShow.value) {
        //判断点击在组件内还是组件外
        isShow.value = (document.getElementById("dropDownList")).contains(e.target)
    }
}
window.addEventListener('click', mouseClick, true)
onBeforeUnmount(() => {
    window.removeEventListener('click', mouseClick, true);
})
//暴露给父组件***
defineExpose({ show })
</script>

<template>
    <teleport to='body'>
        <transition name="opacity">
            <div :class="bodyClass" id="dropDownList" :style="`left:${page.x}px; top:${page.y}px`" v-if="isShow"
                @mouseenter="mouseEnter" @mouseleave="mouseLeave">
                <div :class="pointClass"
                    :style="`transform: translate(calc(-50% + ${-offset[0]}px),calc(-50% + ${-offset[1]}px)) rotate(45deg);`">
                </div>
                <slot></slot>
            </div>
        </transition>
    </teleport>
</template>

<style scoped>
.body {
    position: absolute;
    z-index: 1001;
    color: var(--color-text-first);
    background-color: var(--color-text-light-first);
    box-shadow: .1em .1em .8em .05em var(--color-text-third);
    padding: .5em .5em;
    border-radius: .5em;
}

.point {
    display: inline-block;
    position: absolute;
    width: .5em;
    height: .5em;
    background-color: var(--color-text-light-first);
    z-index: -1;
}

.top {
    transform: translate(-50%, -110%);
}

.pointtop {
    left: 50%;
    top: 100%;
}

.left {
    transform: translate(-110%, -50%);
}

.pointleft {
    left: -100%;
    top: 50%;
}

.right {
    transform: translate(10%, -50%);
}

.pointright {
    left: 0;
    top: 50%;
}

.bottom {
    transform: translate(-50%, 10%);
}

.pointbottom {
    left: 50%;
    top: 0%;
}
</style>